package netty.rpc.framework.core.netty.transport.server;

import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import netty.rpc.framework.core.netty.exception.NoSuchServiceException;
import netty.rpc.framework.core.netty.factory.SingletonFactory;
import netty.rpc.framework.core.netty.transport.protocol.RPCMessage;
import netty.rpc.framework.core.netty.transport.protocol.RPCRequest;
import netty.rpc.framework.core.netty.transport.protocol.RPCResponse;

import java.lang.reflect.Method;

/**
 * 服务端处理来自客户端发送的服务请求RPCMessage处理类
 */
@Slf4j
public class ServiceHandler extends ChannelInboundHandlerAdapter {
    //默认关闭通道需要经过的空闲次数
    private int readIdleTimes = 3;
    //暂时的服务获取中心，将会在bean初始化时添加进map
    private ServiceProvider serviceProvider = SingletonFactory.getInstance(ServiceProvider.class);

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if(msg instanceof RPCMessage){
            RPCMessage rpcMessage = (RPCMessage) msg;

            RPCRequest rpcRequest = (RPCRequest) rpcMessage.getData();
            String interfaceName = rpcRequest.getInterfaceName();

            Object service = serviceProvider.getService(interfaceName);
            if(service == null){
                log.error("查找 {} 服务失败",interfaceName);
                throw new NoSuchServiceException();
            }

            Method method = service.getClass().getMethod(rpcRequest.getMethodName(), rpcRequest.getParamsTypes());
            Object invoke = method.invoke(service, rpcRequest.getParamsValue());

            RPCResponse rpcResponse = RPCResponse.builder()
                    .code(200)
                    .message("RPC调用成功！")
                    .requestId(rpcRequest.getRequestId())
                    .data(invoke)
                    .build();
            RPCMessage resultMessage = RPCMessage.builder()
                    .data(rpcResponse)
                    .build();
            ctx.writeAndFlush(resultMessage).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            log.info("执行 rpc 调用服务 {} 完成！",interfaceName);
        }
        ReferenceCountUtil.release(msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        RPCResponse rpcResponse = new RPCResponse();
        rpcResponse.setCode(600);
        rpcResponse.setMessage("RPC调用出错了");
        rpcResponse.setRequestId(-1);
        RPCMessage.builder()
                .data(rpcResponse)
                .build();

        ctx.writeAndFlush(rpcResponse);
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if( !(evt instanceof IdleStateEvent)){
            return;
        }
        IdleStateEvent event = (IdleStateEvent)evt;
        String eventType = null;
        switch (event.state()){
            case READER_IDLE:
                eventType = "读空闲";
                readIdleTimes --; // 读空闲的计数加1
                break;
            case WRITER_IDLE:
                eventType = "写空闲";
                // 不处理
                break;
            case ALL_IDLE:
                eventType ="读写空闲";
                // 不处理
                break;
        }

        if(ctx.channel().isActive()){
            log.error( "{} 超时事件：{}" ,ctx.channel().remoteAddress(),eventType);
        }
        if(readIdleTimes == 0  ){
            log.info(" [server]读空闲超过3次，关闭连接");
            RPCResponse rpcResponse = RPCResponse.builder()
                    .code(600)
                    .message("ping")
                    .requestId(-1)
                    .build();
            RPCMessage resultMessage = RPCMessage.builder()
                    .data(rpcResponse)
                    .build();
            ctx.channel().writeAndFlush(rpcResponse).sync();
            ctx.channel().close().sync();
            ctx.disconnect().sync();

        }
    }
}
